package com.fdq.dianping.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fdq.dianping.entity.*;
import com.fdq.dianping.entity.dto.UserDto;
import com.fdq.dianping.mapper.BlogMapper;
import com.fdq.dianping.service.IBlogService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fdq.dianping.service.IFollowService;
import com.fdq.dianping.service.UserService;
import com.fdq.dianping.util.Constants;
import com.fdq.dianping.util.ThreadLocalUtil;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static com.fdq.dianping.util.Constants.*;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author fdq
 * @since 2024-12-29
 */
@Service
public class BlogServiceImpl extends ServiceImpl<BlogMapper, Blog> implements IBlogService {

    @Resource
    private UserService userService;

    @Resource
    private IFollowService followService;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Override
    public Result queryHotBlog(Integer current) {
        //分页查询
        Page<Blog> page = query().orderByDesc("liked").page(new Page<>(current, Constants.Page_Size));
        //获取当前页的数据
        List<Blog> blogs = page.getRecords();
        //对每个博客查询其用户信息
        blogs.forEach(blog -> {
            this.queryBlogUser(blog);
            this.isBlogLiked(blog);
        });
        return Result.success(blogs);
    }

    @Override
    public Result queryBlogById(Long id) {
        Blog blog = getById(id);
        if(blog == null){
            return Result.error("笔记不存在");
        }
        //查询博客的发布用户信息
        queryBlogUser(blog);
        //博客是否被当前用户点赞过
        isBlogLiked(blog);

        return Result.success(blog);
    }

    //给blog的isLiked属性赋值，表示当前用户是否给博客点赞过（前端渲染用）
    private void isBlogLiked(Blog blog) {
        //获取当前登录用户信息
        Map<String,Object> userMap = ThreadLocalUtil.get();
        if(userMap == null){
            //用户没有登录，不用查询其是否点过赞
            return;
        }

        Integer userIdObject =(Integer) userMap.get("id");
        Long userId = Long.valueOf(userIdObject);

        //键为前缀+点评id
        String key = Blog_Like+blog.getId();
        Double score = stringRedisTemplate.opsForZSet().score(key, userId.toString());
        //System.out.println(score);

        blog.setIsLike(score != null);

    }

    @Override
    public Result likeBlog(Long id) {
        //判断当前用户是否点赞过       使用redis的SortedSet数据类型判断,SortedSet相当于一个可以排序的集合,这里按时间排序
        //获取当前登录用户信息
        Map<String,Object> userMap = ThreadLocalUtil.get();
        Integer userIdObject =(Integer) userMap.get("id");
        Long userId = Long.valueOf(userIdObject);

        String key = Blog_Like+id;        //键为前缀+点评id
        Double score = stringRedisTemplate.opsForZSet().score(key, userId.toString());

        if(score == null){
            //如果未点赞可以点赞，点赞+1
            boolean success = update().setSql("liked = liked + 1").eq("id", id).update();
            if(success){
                //在redis的集合set中添加已经点赞的用户id
                stringRedisTemplate.opsForZSet().add(key,userId.toString(),System.currentTimeMillis());
            }
        }else {
            //如果已经点赞，取消点赞，点赞-1
            boolean success = update().setSql("liked = liked - 1").eq("id", id).update();
            if(success){
                //在redis的集合set中删除已经点赞的用户id
                stringRedisTemplate.opsForZSet().remove(key,userId.toString());
            }
        }

        return Result.success();
    }

    @Override
    public Result queryBlogLikes(Long id) {
        String key = Blog_Like+id;        //键为前缀+点评id
        //查询最近点评的5个用户
        Set<String> top5 = stringRedisTemplate.opsForZSet().range(key, 0, 4);
        if(top5 == null || top5.isEmpty()){
            return Result.success();
        }

        List<Long> ids = top5.stream().map(Long::valueOf).collect(Collectors.toList());
        List<UserDto> userDtos = userService.listByIds(ids)
                .stream()
                .map(user -> BeanUtil.copyProperties(user, UserDto.class))
                .toList();
        return Result.success(userDtos);
    }

    private void queryBlogUser(Blog blog) {
        Long userId = blog.getUserId();
        User user = userService.getById(userId);
        blog.setIcon(user.getIcon());
        blog.setName(user.getNickName());
    }



    @Override
    public Result saveBlog(Blog blog) {
        //获取当前登录用户信息
        Map<String,Object> userMap = ThreadLocalUtil.get();
        UserDto user = BeanUtil.mapToBean(userMap,UserDto.class,true);
        blog.setUserId(user.getId());
        //保存笔记
        boolean isSuccess = save(blog);
        if(!isSuccess){
            return Result.error("新增笔记失败");
        }

        //查询发布笔记的用户的全部粉丝
        List<Follow> followers = followService.query().eq("follow_user_id", user.getId()).list();

        //将点评发送到粉丝的收件箱
        followers.forEach(follow -> {
            //获取粉丝id
            Long followUserId = follow.getUserId();
            //在redis中保存
            String key = Feed_Prefix+followUserId;
            stringRedisTemplate.opsForZSet().add(key,blog.getId().toString(),System.currentTimeMillis());

        });
        return Result.success();
    }

    @Override
    public Result queryBlogOfFollow(Long max, Integer offset) {
        //1.获取当前用户
        //获取当前登录用户信息
        Map<String,Object> userMap = ThreadLocalUtil.get();
        UserDto user = BeanUtil.mapToBean(userMap,UserDto.class,true);
        //2.查询收件箱
        String key = Feed_Prefix+user.getId();
        //此处实现思路较为复杂，不懂的话看b站第87个视频
        Set<ZSetOperations.TypedTuple<String>> typedTuples = stringRedisTemplate.opsForZSet()
                .reverseRangeByScoreWithScores(key, 0, max, offset, Feed_page);
        //非空判断
        if(typedTuples == null || typedTuples.isEmpty()){
            return Result.success();
        }
        //3.解析数据：blogId,minTime时间戳,offset
        List<Long> ids = new ArrayList<>(typedTuples.size());
        long minTime = 0;
        int os = 1;
        for (ZSetOperations.TypedTuple<String> typedTuple : typedTuples) {
            //获取id
            ids.add(Long.valueOf(typedTuple.getValue()));
            //获取时间戳
            long time = typedTuple.getScore().longValue();
            if(time == minTime){
                os++;
            }else {
                minTime = time;
                os = 1;
            }
        }

        //4.根据id查询blog
        String idsStr = StrUtil.join(",",ids);
        //使查询结果按数组ids的顺序排列
        List<Blog> blogs = query().in("id", ids).last("ORDER BY FIELD (id," + idsStr + ")").list();

        //完善blog信息
        for (Blog blog : blogs) {
            //查询博客的发布用户信息
            queryBlogUser(blog);
            //博客是否被当前用户点赞过
            isBlogLiked(blog);
        }

        //5.封装scrollResult
        ScrollResult scrollBlog = new ScrollResult();
        scrollBlog.setList(blogs);
        scrollBlog.setOffset(os);
        scrollBlog.setMinTime(minTime);
        return Result.success(scrollBlog);
    }


}
